package cn.bluech.lib.utils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * Description: 反射操作类
 * Create by Codeant on 2019/12/1 0001
 */
public class ReflectUtils {

    /**
     * 获取静态属性
     * @param clsName 目标类名(完整路径)
     * @param fieldName 目标属性名
     */
    public static <T> T get(String clsName, String fieldName) throws Throwable {
        Field field;
        try {
            Class cls = Class.forName(clsName);
            field = cls.getDeclaredField(fieldName);
            field.setAccessible(true);
            return (T) field.get(cls);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new Throwable("Class not found");
        }
    }

    /**
     * 设置静态属性
     * @param clsName 目标类名(完整路径)
     * @param fieldName 目标属性名
     * @param value 要设置的值(注意数据类型)
     */
    public static void set(String clsName, String fieldName, Object value) throws Throwable {
        Field field;
        try {
            Class cls = Class.forName(clsName);
            field = cls.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(cls,value);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Throwable(e.getMessage());
        }
    }

    /**
     * 调用静态方法
     * @param clsName 目标类名(完整路径)
     * @param methodName 方法名
     * @param args 参数
     */
    public static <T> T call(String clsName, String methodName,Object ... args) throws Throwable {
        try {
            Class cls = Class.forName(clsName);
            Method method;

            Class[] argCls = null;
            if((null!=args)&&args.length>0){
                argCls = new Class[args.length];
                for(int i=0;i<args.length;i++) {
                    argCls[i] = args[i].getClass();
                }
            }
            method = cls.getMethod(methodName,argCls);
            if(null==method)
                throw new Throwable("Method not find: "+clsName+"."+methodName);

            method.setAccessible(true);
            return (T)method.invoke(cls,args);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new Throwable("Class not find: "+clsName);
        } catch (Exception e){
            e.printStackTrace();
            throw new Throwable(e.getMessage());
        }
    }


    /**
     * 实例化对象
     * @param clsName 目标类名(完整路径)
     * @param argCls 参数类型
     * @param args 参数
     * @return 实例对象
     */
    public static Object newInstance(String clsName, Class[] argCls, Object... args) throws Throwable {
        try {
            Class cls = Class.forName(clsName);
            Constructor constructor = cls.getDeclaredConstructor(argCls);
            if(null==constructor)
                throw new Throwable("Constructor not found");
            constructor.setAccessible(true);
            return constructor.newInstance(args);
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new Throwable("Class not found");
        } catch (Exception e){
            e.printStackTrace();
            throw new Throwable(e.getMessage());
        }
    }

    /**
     * 实例化对象
     * @param clsName 目标类名(完整路径)
     * @param args 参数,如果传的是子类的对象作为参数将不能自动识别,封装类和基础数据类型也是不一样的,需要手动指定类型
     * @return 实例对象
     */
    public static Object newInstance(String clsName,Object... args) throws Throwable {
        Class[] argCls = null;
        if((null!=args)&&args.length>0){
            argCls = new Class[args.length];
            for(int i=0;i<args.length;i++) {
                argCls[i] = args[i].getClass();
            }
        }

        return newInstance(clsName,argCls,args);
    }

    /**
     * 递归查找属性
     * @param cls 最顶层class
     * @param fieldName 属性名
     */
    private static Field findField(Class cls, String fieldName){
        do {
            try {
                return cls.getDeclaredField(fieldName);
            } catch (NoSuchFieldException ignored) { }
            cls = cls.getSuperclass(); // 从父类中查找
        } while (null != cls);

        return null;
    }

    /**
     * 获取属性值
     * @param instance 目标对象
     * @param fieldName 目标属性名
     */
    public static <T> T get(Object instance, String fieldName) throws Throwable {
        Field field;
        try {
            Class cls = instance.getClass();
            if((field = findField(cls,fieldName))==null)
                throw new Throwable("Field not find");
            field.setAccessible(true);
            return (T) field.get(instance);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Throwable(e.getMessage());
        }
    }

    /**
     * 设置属性值
     * @param instance 目标对象
     * @param fieldName 目标属性名
     * @param value 要设置的值(注意数据类型)
     */
    public static void set(Object instance, String fieldName, Object value) throws Throwable {
        Field field;
        try {
            Class<?> cls = instance.getClass();
            if((field = findField(cls,fieldName))==null)
                throw new Throwable("Field not find");
            field.setAccessible(true);
            field.set(instance,value);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Throwable(e.getMessage());
        }
    }

    public static Method findMethod(Class<?> cls, String methodName, Class<?>... types){
        do {
            try {
                return cls.getMethod(methodName,types);
            } catch (NoSuchMethodException ignored) { }
            cls = cls.getSuperclass(); // 从父类中查找
        } while (null != cls);

        return null;
    }

    /**
     * 调用对象方法
     * @param instance 目标对象
     * @param methodName 目标方法名
     * @param args 参数
     */
    public static <T> T call(Object instance, String methodName, Class[] argCls, Object ... args) throws Throwable {
        try {
            Class cls = instance.getClass();
            Method method;
            if((method = findMethod(cls,methodName,argCls))==null)
                throw new Throwable("Method not find");

            method.setAccessible(true);
            return (T)method.invoke(instance,args);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new Throwable("Class not find");
        } catch (Exception e){
            e.printStackTrace();
            throw new Throwable(e.getMessage());
        }
    }

    /**
     * 调用对象方法
     * @param instance 目标对象
     * @param methodName 目标方法名
     * @param args 参数
     */
    public static <T> T call(Object instance, String methodName,Object ... args) throws Throwable {
        try {
            Class[] argCls = null;
            if((null!=args)&&args.length>0){
                argCls = new Class[args.length];
                for(int i=0;i<args.length;i++) {
                    argCls[i] = args[i].getClass();
                }
            }
            return call(instance,methodName,argCls,args);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new Throwable("Class not find");
        } catch (Exception e){
            e.printStackTrace();
            throw new Throwable(e.getMessage());
        }
    }
}

